package com.universal.base.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.google.common.collect.Lists;
import com.universal.base.entity.BaseEntity;
import com.universal.base.entity.CollectionsEntity;
import com.universal.base.entity.PageEntity;
import com.universal.base.service.IBaseService;
import com.universal.common.utils.ConverterUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import javax.validation.Valid;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.List;

/**
 * BaseController
 *
 * @author austin
 * @data 2022/11/22 11:41
 */
public abstract class BaseController<SERVICE extends IBaseService<T>, T extends BaseEntity, DTO> {

    @Autowired
    private SERVICE service;

    private Class getObjectTClass() {
        return ReflectionKit.getSuperClassGenericType(this.getClass(), 1);
    }

    private Class getObjectDTOClass() {
        return ReflectionKit.getSuperClassGenericType(this.getClass(), 2);
    }

    /**
     * @return
     */
    protected DTO defaultDTOInstance() {
        Class clazz = getObjectDTOClass();
        try {
            return (DTO) clazz.newInstance();
        } catch (InstantiationException | IllegalAccessException ignore) {
        }
        return null;
    }

    protected T defaultTInstance() {
        Class clazz = getObjectTClass();
        try {
            return (T) clazz.newInstance();
        } catch (InstantiationException | IllegalAccessException ignore) {
        }
        return null;
    }

    @GetMapping(value = "get/{id}")
    public DTO getById(@PathVariable("id") Serializable id) {
        T t = this.service.getById(id);
        if (t != null) {
            return ConverterUtils.beanConvert(t, defaultDTOInstance());
        }
        return null;
    }

    @PostMapping(value = "count")
    public long count(@RequestBody @Valid DTO dto) {
        T entity = ConverterUtils.beanConvert(dto, defaultTInstance());
        return this.service.count(entity);
    }

    @PostMapping(value = "getOne")
    public DTO getOne(@RequestBody @Valid DTO dto) {
        T entity = ConverterUtils.beanConvert(dto, defaultTInstance());
        T t = this.service.getOne(entity);
        if (t != null) {
            return ConverterUtils.beanConvert(t, defaultDTOInstance());
        }
        return null;
    }

    @PostMapping(value = "list")
    public List<DTO> list(@RequestBody @Valid DTO dto) {
        T entity = ConverterUtils.beanConvert(dto, defaultTInstance());

        List<T> list = this.service.list(new QueryWrapper(entity));
        if (CollectionUtils.isEmpty(list)) {
            return Lists.newArrayList();
        }

        Class clazz = getObjectDTOClass();
        List<DTO> convertList = ConverterUtils.listConvert(list, clazz);
        return convertList;
    }

    @PostMapping(value = "listByCollectionEntity")
    public List<DTO> listByCollectionEntity(@RequestBody @Valid CollectionsEntity<DTO> collectionsEntity) {
        DTO dto = collectionsEntity.getEntity();

        CollectionsEntity<T> newCollectionsEntity = new CollectionsEntity<>();
        BeanUtils.copyProperties(collectionsEntity, newCollectionsEntity);
        newCollectionsEntity.setEntity(ConverterUtils.beanConvert(dto, defaultTInstance()));
        newCollectionsEntity.setOrderBy(collectionsEntity.getOrderBy());
        List<T> list = this.service.list(newCollectionsEntity);
        if (CollectionUtils.isEmpty(list)) {
            return Lists.newArrayList();
        }
        Class clazz = getObjectDTOClass();
        List<DTO> convertList = ConverterUtils.listConvert(list, clazz);
        return convertList;
    }

    @PostMapping(value = "page")
    public PageEntity<DTO> page(@RequestBody @Valid PageEntity<DTO> pageEntity) {
        DTO dto = pageEntity.getEntity();

        PageEntity<T> newPageEntity = new PageEntity<>();
        BeanUtils.copyProperties(pageEntity, newPageEntity);
        newPageEntity.setCurrent(pageEntity.getCurrent());
        newPageEntity.setSize(pageEntity.getSize());
        newPageEntity.setEntity(ConverterUtils.beanConvert(dto, defaultTInstance()));
        PageEntity<DTO> resultPageEntity = new PageEntity<>();
        IPage<T> page = this.service.page(newPageEntity);
        if (page != null) {
            BeanUtils.copyProperties(page, resultPageEntity);
            Class clazz = getObjectDTOClass();
            List<DTO> records = ConverterUtils.listConvert(page.getRecords(), clazz);
            resultPageEntity.setRecords(records);
        }
        return resultPageEntity;
    }

    @PostMapping(value = "saveOrUpdateBatch")
    public boolean saveOrUpdateBatch(@RequestBody @Valid List<DTO> list) {
        Class clazz = getObjectTClass();
        List<T> newList = ConverterUtils.listConvert(list, clazz);
        return this.service.saveOrUpdateBatch(newList);
    }

    @PostMapping(value = "saveBatch")
    public boolean saveBatch(@RequestBody @Valid List<DTO> list) {
        Class clazz = getObjectTClass();
        List<T> newList = ConverterUtils.listConvert(list, clazz);
        return this.service.saveBatch(newList);
    }

    @PostMapping(value = "save")
    public DTO save(@RequestBody @Valid DTO dto) {
        T entity = ConverterUtils.beanConvert(dto, defaultTInstance());
        boolean i = this.service.save(entity);
        return i ? ConverterUtils.beanConvert(entity, defaultDTOInstance()) : null;
    }

    @PostMapping(value = "saveOrUpdate")
    public DTO saveOrUpdate(@RequestBody @Valid DTO dto) {
        T entity = ConverterUtils.beanConvert(dto, defaultTInstance());
        setUpdateDefault(entity);
        boolean i = this.service.saveOrUpdate(entity);
        return i ? ConverterUtils.beanConvert(entity, defaultDTOInstance()) : null;
    }


    @DeleteMapping(value = "delete/{id}")
    public boolean removeById(@PathVariable("id") Serializable id) {
        return this.service.removeById(id);
    }

    @PostMapping(value = "updateById")
    public boolean updateById(@RequestBody @Valid DTO dto) {
        T entity = ConverterUtils.beanConvert(dto, defaultTInstance());
        setUpdateDefault(entity);
        return this.service.updateById(entity);
    }

    public static <T extends BaseEntity> void setUpdateDefault(T t) {
        t.setUpdateTime(LocalDateTime.now());
    }


}
